// 定义业务错误类型
import type {
  AxiosError,
  AxiosInstance,
  AxiosRequestHeaders,
  AxiosResponse,
  CancelTokenSource,
  InternalAxiosRequestConfig
} from "axios";
import type {HttpConfig, ResponseData} from "@/requests/http/http.type.ts";
import axios from "axios";
import {BusinessError} from "@/requests/http/http.type.ts";
import {ElLoading, ElMessage,} from "element-plus";
import type {LoadingInstance} from 'element-plus';

// 默认配置
const DEFAULT_CONFIG: HttpConfig = {
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json',
  } as AxiosRequestHeaders,
};

export class Http {
  private instance: AxiosInstance;
  private pendingRequests: Map<string, CancelTokenSource>;
  private loadingCount: number = 0;
  private loadingInstance = {} as LoadingInstance;

  constructor(config: HttpConfig = DEFAULT_CONFIG) {
    this.instance = axios.create(config);
    this.pendingRequests = new Map();

    // 请求拦截器
    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig<any>) => this.handleRequest(config),
      (error) => this.handleRequestError(error) // Promise.reject(error)
    );

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response) => this.handleResponse(response),
      (error) => this.handleResponseError(error)
    );
  }

  /**
   * 处理请求配置（可被子类重写）
   */
  handleRequest(config: HttpConfig): HttpConfig {
    // 添加请求唯一标识
    const requestKey = `${config.method}-${config.url}`;

    // 取消重复请求
    this.cancelPendingRequest(requestKey);

    // 创建取消令牌
    const source = axios.CancelToken.source();
    config.cancelToken = source.token;
    this.pendingRequests.set(requestKey, source);

    // 显示加载
    if (config?.showLoading) {
      this.showLoading()
    }

    /*
    // 默认添加Token（子类可重写）
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
     */

    return config;
  }

  /**
   * 处理响应（可被子类重写）
   */
  handleResponse<T>(response: AxiosResponse<ResponseData<T>>): T {
    const config = response.config as HttpConfig;
    const requestKey = `${config.method}-${config.url}`;

    // 移除已完成请求
    this.pendingRequests.delete(requestKey);

    // 隐藏加载提示
    if (config.showLoading) {
      this.hideLoading();
    }

    const {code, message, data} = response.data

    // 业务状态码判断
    if (code === 200) {
      return data;
    } else {
      // 抛出业务异常
      throw new BusinessError(message, code, response) // 配合未来 api 接口中的 try...catch... 使用
    }
  }


  // 请求错误处理（子类可重写）
  handleRequestError(error: AxiosError) {
    ElMessage.error('请求参数错误')
    return Promise.reject(error)
  }

  /**
   * 处理响应错误（子类可重写）
   */
  handleResponseError(error: AxiosError) {
    const config = error.config as HttpConfig;
    const requestKey = `${config?.method}-${config?.url}`;

    // 移除已完成请求
    if (requestKey) {
      this.pendingRequests.delete(requestKey);
    }

    // 隐藏加载提示
    if (config?.showLoading) {
      this.hideLoading();
    }

    // 处理取消请求的情况
    if (axios.isCancel(error)) {
      return Promise.reject(new Error('请求已取消'));
    }

    // 显示错误提示
    if (config?.showError) {
      this.showError(error);
    }

    // 根据状态码处理不同错误
    if (error.response) {
      const status = error.response.status;

      switch (status) {
        case 401:
          this.handleUnauthorized();
          break;
        case 403:
          this.handleForbidden();
          break;
        case 500:
          this.handleServerError();
          break;
        default:
          console.error('HTTP 错误:', status);
          ElMessage.error(`HTTP 错误: ${status}`);
      }
    } else if (error.request) {
      console.error('网络错误:', error.message);
      ElMessage.error(`网络错误: ${error.message}`);
    } else {
      console.error('请求错误:', error.message);
      ElMessage.error(`请求错误: ${error.message}`)
    }

    return Promise.reject(error);
  }

  /**
   * 取消重复请求
   */
  private cancelPendingRequest(key: string) {
    if (this.pendingRequests.has(key)) {
      const source = this.pendingRequests.get(key);
      source?.cancel('取消重复请求');
      this.pendingRequests.delete(key);
    }
  }

  /**
   * 显示加载提示
   */
  private showLoading() {
    this.loadingCount++;
    if (this.loadingCount === 1) {
      // 实现全局加载提示
      console.log('显示全局加载提示');
      this.loadingInstance = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.1)'
      })
    }
  }

  /**
   * 隐藏加载提示
   */
  private hideLoading() {
    this.loadingCount = Math.max(0, this.loadingCount - 1);
    if (this.loadingCount === 0) {
      // 隐藏全局加载提示
      console.log('隐藏全局加载提示');
      this.loadingInstance?.close()
    }
  }

  /**
   * 显示错误提示
   */
  private showError(error: AxiosError | BusinessError) {
    let message = '请求失败，请稍后重试';

    if (error instanceof BusinessError) {
      message = error.message;
    } else if (error.response) {
      const status = error.response.status;
      if (status === 404) message = '资源不存在';
      else if (status === 500) message = '服务器错误';
    }

    // 实现错误提示
    console.error('显示错误提示:', message);
    ElMessage.error(`显示错误提示: ${message}`)
  }

  /**
   * 处理未授权
   */
  private handleUnauthorized() {
    console.log('处理未授权逻辑');
    // 例如：跳转到登录页面
    ElMessage.error('处理未授权逻辑')
  }

  /**
   * 处理禁止访问
   */
  private handleForbidden() {
    console.log('处理禁止访问逻辑');
    // 例如：显示权限不足提示
    ElMessage.error('处理禁止访问逻辑')
  }

  /**
   * 处理服务器错误
   */
  private handleServerError() {
    console.log('处理服务器错误逻辑');
    // 例如：显示服务器错误页面
    ElMessage.error('处理服务器错误逻辑');
  }

  /**
   * 发送请求
   */
  public request<T = any>(config: HttpConfig): Promise<T> {
    return this.instance.request({
      ...DEFAULT_CONFIG,
      ...config,
    });
  }

  /**
   * GET 请求
   */
  public get<T = any>(url: string, params?: any, config?: HttpConfig): Promise<T> {
    return this.request({
      url,
      params,
      method: 'GET',
      ...config,
    } as HttpConfig);
  }

  /**
   * POST 请求
   */
  public post<T = any>(url: string, data?: any, config?: HttpConfig): Promise<T> {
    return this.request({
      url,
      data,
      method: 'POST',
      ...config,
    } as HttpConfig);
  }

  /**
   * PUT 请求
   */
  public put<T = any>(url: string, data?: any, config?: HttpConfig): Promise<T> {
    return this.request({
      url,
      data,
      method: 'PUT',
      ...config,
    } as HttpConfig);
  }

  /**
   * DELETE 请求
   */
  public delete<T = any>(url: string, config?: HttpConfig): Promise<T> {
    return this.request({
      url,
      method: 'DELETE',
      ...config,
    } as HttpConfig);
  }

  /**
   * PATCH 请求
   */
  public patch<T = any>(url: string, data?: any, config?: HttpConfig): Promise<T> {
    return this.request({
      url,
      data,
      method: 'PATCH',
      ...config,
    } as HttpConfig);
  }

  /**
   * 上传文件
   */
  public upload<T = any>(url: string, file: File, config?: HttpConfig): Promise<T> {
    const formData = new FormData();
    formData.append('file', file);

    return this.post(url, formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      } as AxiosRequestHeaders,
      ...config,
    });
  }

  /**
   * 下载文件
   */
  public download(url: string, filename: string, config?: HttpConfig): Promise<void> {
    return this.get(url, {
      responseType: 'blob',
      ...config,
    }).then((response) => {
      const blob = new Blob([response]);
      const link = document.createElement('a');
      link.href = URL.createObjectURL(blob);
      link.download = filename;
      link.click();
      URL.revokeObjectURL(link.href);
    });
  }
}
